package com.miao.executor;

import com.miao.config.BoundSql;
import com.miao.pojo.Configuration;
import com.miao.pojo.MappedStatement;
import com.miao.utils.GenericTokenParser;
import com.miao.utils.ParameterMapping;
import com.miao.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 喵瑞恒
 * @date 2023/09/13 21:37
 **/
public class SimpleExecutor implements Executor {

    private Connection connection = null;
    private PreparedStatement preparedStatement = null;
    private ResultSet resultSet = null;

    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object param) throws Exception {

        //1.加载驱动，获取数据库连接
        connection = configuration.getDataSource().getConnection();

        //2.获取preparedStatement预编译对象
        //获取要执行的sql语句
        String sql = mappedStatement.getSql();
        //把sql中的#{}替换成？，并将#{}中的值保存下来
        BoundSql boundSql = getBoundSql(sql);
        String finalSql = boundSql.getFinalSql();

        preparedStatement = connection.prepareStatement(finalSql);

        //3.设置参数
        // com.miao.pojo.User
        String parameterType = mappedStatement.getParameterType();

        if (parameterType != null) {
            Class<?> parameterTypeClass = Class.forName(parameterType);
            List<ParameterMapping> parameterMappingList = boundSql.getList();

            int i = 1;
            for (ParameterMapping parameterMapping : parameterMappingList) {
                String paramName = parameterMapping.getContent();  // id || username
                //反射
                Field declaredField = parameterTypeClass.getDeclaredField(paramName);
                //暴力访问
                declaredField.setAccessible(true);
                Object value = declaredField.get(param);
                //赋值占位符
                preparedStatement.setObject(i, value);
                i++;
            }
        }

        //4.执行sql，发起查询
        resultSet = preparedStatement.executeQuery();

        //5.处理返回结果集
        List<E> list = new ArrayList<E>();
        while (resultSet.next()) {
            // 元数据信息 包含了 字段名以及字段的值
            ResultSetMetaData metaData = resultSet.getMetaData();

            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = Class.forName(resultType);
            Object o = resultTypeClass.newInstance();

            for (int j = 1; j <= metaData.getColumnCount(); j++) {
                //获取查询到的字段名
                String columnName = metaData.getColumnName(j);
                //获取查询到的字段值
                Object value = resultSet.getObject(columnName);

                //获取要封装成的结果对象并封装
                //属性描述器，通过API方法获取某个属性的读写方法
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //参数1：实例对象，参数2：要设置的值
                writeMethod.invoke(o, value);
            }
            list.add((E) o);
        }
        return list;
    }

    /**
     * 1.将#{}替换成？  2.将#{}中的值保存下来
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //1.创建标记处理器，配合标记解析器完成标记的处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        //2.创建标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);

        // finalSql中的#{}已经完成替换，  并且将里边的值保存到了ParameterMapping对象中
        String finalSql = genericTokenParser.parse(sql);

        // #{}里边的值的集合 id username
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(finalSql, parameterMappings);

        return boundSql;
    }

    /**
     * 释放资源
     */
    public void close() {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (preparedStatement != null) {
            try {
                resultSet.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                resultSet.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
